class Vue {
  constructor(obj_instance) {
    this.$data = obj_instance.data;
    Observer(this.$data);
    Compile(obj_instance.el, this);
  }
}

// 数据劫持 - 监听Vue实例里的数据
function Observer(data_instance) {
  if (!data_instance || typeof data_instance !== 'object') return;
  const dependency = new Dependency();
  Object.keys(data_instance).forEach((key) => {
    let value = data_instance[key];
    Observer(value);

    Object.defineProperty(data_instance, key, {
      enumerable: true,
      configurable: true,
      get() {
        Dependency.temp && dependency.addSub(Dependency.temp);
        return value;
      },
      set(newValue) {
        value = newValue;
        Observer(newValue);
        dependency.notify();
      }
    })
  })
}

// HTML模板解析 - 替换DOM里的数据
function Compile(element, vm) {
  vm.$el = document.querySelector(element);
  const fragment = document.createDocumentFragment();
  while (vm.$el.firstChild) {
    fragment.appendChild(vm.$el.firstChild);
  }
  fragment_compile(fragment);

  function fragment_compile(node) {
    const pattern = /\{\{\s*(\S+)\s*\}\}/;
    if (node.nodeType === 3) {  
      const xxx = node.nodeValue;
      const result_regex = pattern.exec(node.nodeValue);
      // console.log('result_regex', result_regex)
      if (result_regex) {
        const arr = result_regex[1].split('.');
        const value = arr.reduce((total, current) => total[current], vm.$data);
        node.nodeValue = xxx.replace(pattern, value);

        // 创建订阅者实例
        new Watcher(vm, result_regex[1], (newValue) => {
          node.nodeValue = xxx.replace(pattern, newValue);
        })
      }
      return;
    }
    if (node.nodeType === 1 && node.nodeName === 'INPUT') {
      const attrs = Array.from(node.attributes);
      attrs.forEach(attr => {
        if (attr.nodeName === 'v-model') {
          node.value = attr.nodeValue.split('.').reduce((total, current) => total[current], vm.$data);
          // 创建订阅者实例
          new Watcher(vm, attr.nodeValue, (newValue) => {
            node.value = newValue;
          });

          // 视图改变数据：
          node.addEventListener("input", e => {
            vm.$data[attr.nodeValue] = e.target.value;  // 只适用于基本属性名，如果属性是多层嵌套的这样处理是不行的！
            // TODO: 如果是链式的属性，比如: a.b.c这样的属性，该如何处理？
          })
        }
      });
    }
    node.childNodes.forEach((child) => fragment_compile(child));
  }
  vm.$el.appendChild(fragment)
}

class Dependency {
  constructor() {
    this.subscribers = [];
  }
  addSub(sub) {
    this.subscribers.push(sub);
  }
  notify() {
    this.subscribers.forEach(sub => sub.update());
  }
}

class Watcher {
  constructor (vm, key, callback) {
    this.vm = vm;
    this.key = key;
    this.callback = callback;
    Dependency.temp = this;
    key.split('.').reduce((total, current) => total[current], vm.$data);
    Dependency.temp = null;
  }
  
  update() {
    const value = this.key.split('.').reduce((total, current) => total[current], this.vm.$data);
    this.callback(value);
  }
}